package com.wzx.user.utils;

/**
 * @author wzx
 * @version 1.0
 * @date 2023/4/24 - 21:59
 * @description 标题
 */

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.client.naming.utils.RandomUtils;
import com.wzx.admin.pojo.RedPkgControl;
import com.wzx.common.constants.CoreConstants;
import com.wzx.common.exception.LeadNewsException;
import com.wzx.user.dto.RedPkgDto;
import com.wzx.user.pojo.RedPkg;
import com.wzx.user.service.RedisControlService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author: huangyibo
 * @Date: 2022/6/23 17:59
 * @Description: 拆红包工具类 二倍均值法(微信红包采用此法)
 * 还是以10元10个红包为例，去除每个红包的最小金额后，红包剩余9.9元，二倍均值计算公式：2 * 剩余金额/剩余红包数
 * 第一个红包在[0,1.98]范围随机，假设随机得1.9，则第一个红包金额为2.0，红包剩余8元。
 * 第二个红包在[0,2]范围随机，假设随机的1元，则第二个红包金额为1.1元，红包剩余7元。
 * 第三个红包在[0,2]范围随机，假设随机的0.5元，则第三个红包金额为0.6元，红包剩余5.5元。
 * 以此类推。
 * <p>
 * 此工具类使用版本——原理
 * 剩余红包金额M，剩余人数N，那么：每次抢到金额=随机(0，M/N*2)
 * 保证了每次随机金额的平均值是公平的
 * 假设10人，红包金额100元
 * 第一人：100/10*2=20，随机范围(0,20)，平均可以抢到10元
 * 第二人：90/9*2=20，随机范围(0,20)，平均可以抢到10元
 * 第三人：80/8*2=20，随机范围(0,20)，平均可以抢到10元
 * 以此类推，每次随机范围的均值是相等的
 * <p>
 * 缺点：除了最后一次，任何一次抢到的金额都不会超过人均金额的两倍，并不是任意的随机
 */

@Component
@Slf4j
public class RedPacketUtils {

    /**
     * 红包的缓存key
     */
    public static final String RED_PACKAGE_KEY = "RED_PACKAGE_KEY_";

    /**
     * 抢红包资格缓存key
     */
    public static final String GRAB_RED_PACKAGE_KEY = "GRAB_RED_PACKAGE_KEY_";

    /**
     * 拆红包的分布式锁key
     */
    public static final String UNPACK_RED_PACKAGE_KEY = "UNPACK_RED_PACKAGE_KEY_";

    /**
     * 发红包的分布式锁key
     */
    public static final String SEND_RED_PACKAGE_KEY = "SEND_RED_PACKAGE_KEY_";

    /**
     * 抢中红包的缓存key
     */
    public static final String RED_PACKAGE_CONSUME_KEY = "RED_PACKAGE_CONSUME_KEY_";

    /**
     * 红包缓存时效时间(没人抢、最大为一天, 红包抢完缓存主动删除)，毫秒
     */
    public static final int RED_PACKET_EXPIRE = 24 * 60 * 60;

    private static final String defaultTitle = "大吉大利";

    /**
     * 收到单个红包最大值
     */
    private static final int MAX_AMOUNT = 20000;

    @Autowired
    private RedisUtils redisUtil;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisControlService redisControlService;

    private final Random random = new Random();


    /**
     * 生成红包数组
     *
     * @param amount 红包总金额（单位分）
     * @param count  发包个数
     * @param type   发包类型（2、随机红包，1、定额红包）
     * @param title  红包标题
     * @return 红包数组
     */
    public int[] generateArr(Integer amount, Integer count, String type, String title, Integer groupId, Integer userId) throws LeadNewsException {
        // 定义返回的数组
        int[] array = new int[count];

        if (count == 1) {
            return new int[]{amount};
        }

        switch (type) {
            case "2":
                if (amount > 100) {
                    array = createRedPkgByLuckMoreAmount(array, amount, count, title, groupId, userId);
                } else {
                    array = createRedPkgByLuckFewAmount(array, amount, count);

                }
                //打乱顺序
                Integer[] newNums = Arrays.stream(array).boxed().toArray(Integer[]::new);
                List<Integer> list = Arrays.asList(newNums);
                Collections.shuffle(list);
                array = list.stream().mapToInt(Integer::intValue).toArray();

                break;
            case "1":
                // 定额红包校验
                array = new int[count];
                Arrays.fill(array, amount / count);
                break;
            case "3":
                //指定红包
                array = new int[1];
                array[0] = amount;
                break;
            default:
                throw new LeadNewsException("类型错误！");
        }

        return array;
    }

    /**
     * 1，随机抢包，钱包低于1元时调用。
     *
     * @param array
     * @param amount
     * @param count
     * @return
     */
    private static int[] createRedPkgByLuckFewAmount(int[] array, Integer amount, Integer count) {
        // 默认分配1至每一位
        for (int i = 0; i < array.length; i++) {
            array[i] = 1;
        }
        int surplus_currency = amount - array.length,// 剩余金额数
                surplus_number = array.length;// 剩余需追加的数量

        for (int i = 0; i < array.length; i++) {

            // 没值可以追加了
            if (new Integer(0).equals(surplus_currency)) {
                break;
            }

            // (总数-（总包-i）*最小值) / (总包 - i) 随机安全值算法
            int safe_total = (int) Math.floor((amount - (count - i)) / (count - i));
            if (new Integer(0).equals(safe_total)) {// 随机值不能为0
                safe_total = 1;
            }
            // 该次随机值
            int randomint = surplus_currency >= safe_total - 1 ? safe_total : surplus_currency + 1;
            // 下次可能最大能剩余值
            int nextMax_currency = (MAX_AMOUNT - 1) * (surplus_number - 1);
            // 最小的随机数     剩余金额-剩余最大随机的总数（不含这一次）
            int minRandom = surplus_currency - nextMax_currency;
            if (minRandom < 0) {
                minRandom = 0;
            }

            // 规避一些特殊情况,每个接近2000或1时会发生
            boolean must = (surplus_currency - count * MAX_AMOUNT <= 2 && surplus_currency - count * MAX_AMOUNT >= 0)
                    /*|| surplus_currency < packet_number * 2*/;
            // 控制安全随机值           随机安全值不能大于最大限制，并且不能小于最小限 制
            if (safe_total < minRandom || safe_total > MAX_AMOUNT || must) {
                safe_total = MAX_AMOUNT;
                // 该次随机值
                randomint = surplus_currency >= safe_total - 1 ? safe_total : surplus_currency + 1;
                // 下次可能最大能剩余值
                nextMax_currency = (randomint - 1) * (surplus_number - 1);
                // 最小的随机数     剩余金额-剩余最大随机的总数（不含这一次）
                minRandom = surplus_currency - nextMax_currency;
                if (minRandom < 0) {
                    minRandom = 0;
                }
            }

            // 下一次最大的随机值
            int nextMaxRandomInt = nextMax_currency - (surplus_currency - (randomint - 1));
            Integer maxRandom = nextMaxRandomInt <= 0 ? nextMaxRandomInt + randomint : null;
            // 能随机          剩余的金额  - 最大随机数 >  最大随机数  * 剩余数量
            boolean canRandom = surplus_currency - (randomint - 1) > nextMax_currency ||
                    nextMaxRandomInt > (randomint - 1)
                    || !new Integer(0).equals(minRandom);

            int addNumber; // 追加的金额
            if (canRandom && !new Integer(randomint).equals(minRandom + 1) && !(new Integer(randomint).equals(minRandom) && new Integer(safe_total).equals(minRandom))) {
                addNumber = myRandom(minRandom, maxRandom == null ? randomint : maxRandom - 1);
            } else {
                addNumber = randomint - 1;
            }
            array[i] += addNumber;
            surplus_currency -= addNumber;
            surplus_number--;
        }
        return array;
    }


    /**
     * 2，随机抢包，钱包高于1元时调用
     *
     * @param m 总金额
     * @param n 分包数
     * @return 每个包的金额
     */
    /*public static int[] createRedPkgByLuckMoreAmount(String title, Integer m, Integer n) {
        List<Integer> list = new ArrayList<>();
        Random random = new Random();
        getAmount(m, n, random, list);
        return list.stream().mapToInt(Integer::intValue).toArray();
    }*/

    /**
     * 分配金额
     *
     * @param m      剩余红包
     * @param n      分包数
     * @param random 随机对象
     * @param list   各包金额
     */
    public static void getAmount(Integer m, Integer n, Random random, List<Integer> list) {
        //1，计算剩余金额平均值
        int avgAmount = m / n;

        //每个用户至少分到1分
        int amount = 1;
        m = m + 1;

        if (avgAmount > 1) {
            int minAmount = (int) (avgAmount * 0.5);
            int maxAmount = (int) (avgAmount * 1.5);

            // 随机生成红包金额
            int randomAmount = random.nextInt(maxAmount - minAmount + 1) + minAmount;
            amount = amount + randomAmount;
        }

        if (n == 1) {
            list.add(m);
        } else {
            m = m - amount;
            n = n - 1;
            list.add(amount);
            getAmount(m, n, random, list);
        }
    }


    public int[] createRedPkgByLuckMoreAmount(int[] array, int amount, int count, String title, int groupId, int userId) {
        //1，查询用户是否设置了，没有则跳过
        String redisKey = CoreConstants.CoreControl.CONTROL_SEND_PREFIX + groupId + "-" + userId;
        log.info("用户：{}，在群聊：{}，中抢包", userId, groupId);
        RedPkgControl redPkgControl = (RedPkgControl) redisTemplate.opsForValue().get(redisKey);
        if (redPkgControl == null) {
            //2，没有设置过，直接返回即可
            log.info("用户：{}，没有设置过拥有，跳过即可", userId);
            return myCreateHb(array, amount, count);
        }

        //2.1，判断 倍率
        String magnification = redPkgControl.getPercentage();
        int percentageNum = Integer.parseInt(magnification) / 10;
        if (redPkgControl.getRobNum() != null && percentageNum <= redPkgControl.getRobNum()) {
            log.info("用户：{}，躲避倍率已达上线，跳过即可", userId);
            boolean randomBoolean = getRandomBoolean(magnification);
            log.info("用户：{}达到上限，判断该次是否可以含有：{}", userId, randomBoolean);
            if (!randomBoolean) {
                return myCreateHb(array, amount, count);
            }
        }

        //增加一次
        if (redPkgControl.getRobNum() == null) {
            redPkgControl.setRobNum(1);
        } else if (redPkgControl.getRobNum() >= 10) {
            //重置
            log.info("用户：{}的次数重置", userId);
            redPkgControl.setRobNum(1);
        } else {
            redPkgControl.setRobNum(redPkgControl.getRobNum() + 1);
        }
        redisTemplate.opsForValue().set(redisKey, redPkgControl);

        //3，根据雷值计算红包
        if (title.equals(defaultTitle)) {
            //1.1，默认雷值 1
            return myCreateHbByOne(array, amount, count, "1");
        } else {
            String totalAmountStr = amount / 100 + "";
            boolean isMatched = title.startsWith(totalAmountStr);
            if (isMatched) {
                //1.2，标题带雷值
                String mine = title.substring(totalAmountStr.length());
                String[] mineArr = extractNumbers(mine);
                String mineValue = mineArr[0];
                if (mineValue.length() > 1) {
                    return myCreateHbByMore(array, amount, count, mineValue);
                } else {
                    return myCreateHbByOne(array, amount, count, mineValue);
                }
            } else {
                //1.3，标题全为雷
                if (title.length() > 1) {
                    return myCreateHbByMore(array, amount, count, title);
                } else {
                    return myCreateHbByOne(array, amount, count, title);
                }
            }
        }

        //return myCreateHb(array, amount, count);
    }


    public int[] myCreateHb(int[] array, Integer amount, Integer count) {
        //保证每个用户先分的一分
        amount = amount - count;
        for (int i = 0; i < array.length; i++) {
            //1，计算剩余金额平均值
            int avgAmount = amount / count;

            //每个用户至少分到1分
            int userAmount = 1;
            amount = amount + 1;

            if (avgAmount > 1) {
                int minAmount = (int) (avgAmount * 0.5);
                int maxAmount = (int) (avgAmount * 1.5);

                // 随机生成红包金额
                int randomAmount = this.random.nextInt(maxAmount - minAmount + 1) + minAmount;
                userAmount = userAmount + randomAmount;
            }

            if (count == 1) {
                array[i] = amount;
            } else {
                amount = amount - userAmount;
                count = count - 1;
                array[i] = userAmount;
            }
        }
        return array;
    }

    public int[] myCreateHbByOne(int[] array, Integer amount, Integer count, String mineValue) {
        //保证每个用户先分的一分
        amount = amount - count;
        for (int i = 0; i < array.length; i++) {
            //1，计算剩余金额平均值
            int avgAmount = amount / count;

            //每个用户至少分到1分
            int userAmount = 1;
            amount = amount + 1;

            if (avgAmount > 1) {
                int minAmount = (int) (avgAmount * 0.5);
                int maxAmount = (int) (avgAmount * 1.5);

                // 随机生成红包金额
                int randomAmount = this.random.nextInt(maxAmount - minAmount + 1) + minAmount;
                userAmount = userAmount + randomAmount;
            }

            if (i == 0) {
                //如果是第一个，则替换
                userAmount = userAmount - (userAmount % 10) + Integer.parseInt(mineValue);
            }

            if (count == 1) {
                array[i] = amount;
            } else {
                amount = amount - userAmount;
                count = count - 1;
                array[i] = userAmount;
            }
        }
        return array;
    }

    public int[] myCreateHbByMore(int[] array, Integer amount, Integer count, String mineValue) {
        //保证每个用户先分的一分
        amount = amount - count;
        char[] charArray = mineValue.toCharArray();
        for (int i = 0; i < array.length; i++) {
            //1，计算剩余金额平均值
            int avgAmount = amount / count;

            //每个用户至少分到1分
            int userAmount = 1;
            amount = amount + 1;

            if (avgAmount > 1) {
                int minAmount = (int) (avgAmount * 0.5);
                int maxAmount = (int) (avgAmount * 1.5);

                // 随机生成红包金额
                int randomAmount = this.random.nextInt(maxAmount - minAmount + 1) + minAmount;
                userAmount = userAmount + randomAmount;
            }

            if (i < charArray.length) {
                //如果是第一个，则替换
                userAmount = userAmount - (userAmount % 10) + Integer.parseInt(charArray[i] + "");
            }

            if (count == 1) {
                array[i] = amount;
            } else {
                amount = amount - userAmount;
                count = count - 1;
                array[i] = userAmount;
            }
        }
        return array;
    }


    /*public static void main(String[] args) {
        //100分 随机10份
        int totalAmount = 1000; //总金额 100元，10000分
        int count = 5; //分成10份

        int[] arr = new int[5];

        for (int j = 0; j < 100; j++) {
            if (totalAmount > 100) {
                int[] redPkgByLuckMoreAmount = myCreateHbByMore(arr, totalAmount, count, "123");
                int sum = 0;
                for (int i : redPkgByLuckMoreAmount) {
                    sum += i;
                    System.out.print(i / 100.00 + "   ");
                }
                System.out.println("第" + (j + 1) + "次，总金额：" + sum);
            } else {
                int[] redPkgByLuckFewAmount = createRedPkgByLuckFewAmount(arr, totalAmount, count);
                int sum = 0;
                for (int i : redPkgByLuckFewAmount) {
                    sum += i;
                    System.out.print(i / 100.00 + "   ");
                }
                System.out.println("第" + (j + 1) + "次，总金额：" + sum);
            }
        }


    }*/

    public boolean getRandomBoolean(String magnification) {
        return Integer.parseInt(magnification) > this.random.nextInt(101);
    }


    public static int myRandom(int min, int randomint) {
        if (min == 0) {

            return RandomUtils.nextInt(randomint);
        } else {

            int nextInt = RandomUtils.nextInt(randomint - min);
            return nextInt + min;
        }
    }


    public static String[] extractNumbers(String input) {
        Pattern pattern = Pattern.compile("\\d+");
        Matcher matcher = pattern.matcher(input);
        int count = 0;

        while (matcher.find()) {
            count++;
        }

        String[] numbers = new String[count];
        matcher.reset();

        for (int i = 0; i < count; i++) {
            if (matcher.find()) {
                numbers[i] = matcher.group();
            }
        }

        return numbers;
    }

    public static int[] getAmountArrByOne(Integer amount, Integer count, String value) {
        // 定义返回的数组
        int[] array = new int[count];

        if (count == 1) {
            return new int[]{amount};
        }

        // 默认分配1至每一位
        for (int i = 0; i < array.length; i++) {
            array[i] = 1;
        }
        int surplus_currency = amount - array.length,// 剩余金额数
                surplus_number = array.length;// 剩余需追加的数量

        //int randomInt = new r

        for (int i = 0; i < array.length; i++) {

            // 没值可以追加了
            if (new Integer(0).equals(surplus_currency)) {
                break;
            }

            // (总数-（总包-i）*最小值) / (总包 - i) 随机安全值算法
            int safe_total = (int) Math.floor((amount - (count - i)) / (count - i));
            if (new Integer(0).equals(safe_total)) {// 随机值不能为0
                safe_total = 1;
            }
            // 该次随机值
            int randomint = surplus_currency >= safe_total - 1 ? safe_total : surplus_currency + 1;
            // 下次可能最大能剩余值
            int nextMax_currency = (MAX_AMOUNT - 1) * (surplus_number - 1);
            // 最小的随机数     剩余金额-剩余最大随机的总数（不含这一次）
            int minRandom = surplus_currency - nextMax_currency;
            if (minRandom < 0) {
                minRandom = 0;
            }

            // 规避一些特殊情况,每个接近2000或1时会发生
            boolean must = (surplus_currency - count * MAX_AMOUNT <= 2 && surplus_currency - count * MAX_AMOUNT >= 0)
                    /*|| surplus_currency < packet_number * 2*/;
            // 控制安全随机值           随机安全值不能大于最大限制，并且不能小于最小限 制
            if (safe_total < minRandom || safe_total > MAX_AMOUNT || must) {
                safe_total = MAX_AMOUNT;
                // 该次随机值
                randomint = surplus_currency >= safe_total - 1 ? safe_total : surplus_currency + 1;
                // 下次可能最大能剩余值
                nextMax_currency = (randomint - 1) * (surplus_number - 1);
                // 最小的随机数     剩余金额-剩余最大随机的总数（不含这一次）
                minRandom = surplus_currency - nextMax_currency;
                if (minRandom < 0) {
                    minRandom = 0;
                }
            }

            // 下一次最大的随机值
            int nextMaxRandomInt = nextMax_currency - (surplus_currency - (randomint - 1));
            Integer maxRandom = nextMaxRandomInt <= 0 ? nextMaxRandomInt + randomint : null;
            // 能随机          剩余的金额  - 最大随机数 >  最大随机数  * 剩余数量
            boolean canRandom = surplus_currency - (randomint - 1) > nextMax_currency ||
                    nextMaxRandomInt > (randomint - 1)
                    || !new Integer(0).equals(minRandom);

            int addNumber; // 追加的金额
            if (canRandom && !new Integer(randomint).equals(minRandom + 1) && !(new Integer(randomint).equals(minRandom) && new Integer(safe_total).equals(minRandom))) {
                addNumber = myRandom(minRandom, maxRandom == null ? randomint : maxRandom - 1);
            } else {
                addNumber = randomint - 1;
            }
            array[i] += addNumber;
            surplus_currency -= addNumber;
            surplus_number--;
        }
        return array;
    }


    /**
     * 拆红包核心接口
     *
     * @param redPacketId 红包id
     * @param userId      用户id
     * @return
     */
    public RedPkgDto unpackRedPacket(Integer redPacketId, Integer userId) throws LeadNewsException {
       /* Double redPacketAmount = null;
        RedPkgDto redPkgDto = new RedPkgDto();
        try {
            boolean tryLock = redisUtil.tryLock(UNPACK_RED_PACKAGE_KEY + redPacketId);
            if (!tryLock) {
                //分布式锁获取失败, 将抢红包资格加回去, 让其他人可以去抢
                //redisUtil.incr(GRAB_RED_PACKAGE_KEY + redPacketId, 1);
                return redPkgDto;
            }

            //红包信息缓存
            String redPackageKey = RED_PACKAGE_KEY + redPacketId;

            //获取红包相关信息
            RedPkg redPkg = (RedPkg) redisUtil.get(redPackageKey);
            if (redPkg == null) {
                throw new LeadNewsException("红包被抢光了！");
            }

            //红包分配金额
            System.err.println("redPkg.getGenerateArr() = " + redPkg.getGenerateArr());
            String[] split = redPkg.getGenerateArr().split(",");

            //红包剩下一个
            if (redPkg.getSurplusNum() == 1) {
                //剩余红包数
                redPkg.setSurplusNum(0);
                redPacketAmount = Double.parseDouble(split[split.length - 1]) / 100;
                //剩余金额
                redPkg.setSurplusAmount(0);
            }

            //获取已经抢了的人数
            String grabRedPacketId = GRAB_RED_PACKAGE_KEY + redPacketId;
            List<Integer> redPackage = (List<Integer>) redisUtil.get(grabRedPacketId);
            System.err.println("redPacketAmount0 = " + redPacketAmount);
            if (redPkg.getSurplusNum() > 1) {
                String s = split[redPackage.size()];
                redPkg.setSurplusAmount(AmountUtils.handlerAmountByDouble(redPacketAmount));
                redPkg.setSurplusNum(redPkg.getSurplusNum() - 1);
            }
            System.err.println("redPacketAmount1 = " + redPacketAmount);
            //成功抢到红包
            if (redPacketAmount != null && redPacketAmount > 0) {
                redPackage.add(userId);
                redisUtil.set(RedPacketUtils.GRAB_RED_PACKAGE_KEY + redPkg.getRedPkgId(), redPackage, RedPacketUtils.RED_PACKET_EXPIRE);

                // 红包信息
                redisUtil.set(RedPacketUtils.RED_PACKAGE_KEY + redPkg.getRedPkgId(), redPkg, RedPacketUtils.RED_PACKET_EXPIRE);
            }
            System.err.println("redPacketAmount2 = " + redPacketAmount);
            //红包被抢完，清除缓存
            if (redPkg.getSurplusNum() <= 0) {
                log.info("红包已被抢完, redPacketId:{}, redPacket:{}", redPacketId, JSON.toJSONString(redPkg));
                //抢红包资格缓存key
                String grabRedPackageKey = GRAB_RED_PACKAGE_KEY + redPacketId;
                //抢中红包的缓存key
                String redPackageConsumeKey = RED_PACKAGE_CONSUME_KEY + redPacketId;

                //清楚该红包相关的缓存
                redisUtil.del(redPackageKey, grabRedPacketId, grabRedPackageKey, redPackageConsumeKey);
            }
            System.err.println("redPacketAmount3 = " + redPacketAmount);
            log.info("抢红包SUCCESS, redPacketId:{}, redPacketAmount:{}", redPacketId, redPacketAmount);

            BeanUtils.copyProperties(redPkg,redPkgDto);
            redPkgDto.setUserRodAmount(redPacketAmount);
            return redPkgDto;
        } finally {
            //释放锁
            redisUtil.unLock(UNPACK_RED_PACKAGE_KEY + redPacketId);
        }*/
        return null;
    }
}
